#ifndef __DS_SHIP__
#define __DS_SHIP__

#include <boost/shared_ptr.hpp>

#include <bullet/btBulletCollisionCommon.h>
#include <bullet/btBulletDynamicsCommon.h>

#include <estd/JSL.hpp>

#include "Models.hpp"
#include "Shapes.hpp"
#include "SpaceObject.hpp"

namespace ds {

struct ShipConfiguration {
    std::string id;
    std::string name;
    egl::core::Matrix4x4 view;
};

class Ship: public SpaceObject, public btMotionState {
protected:
    bool loaded;

    egl::core::Matrix4x4                    view;
    boost::shared_ptr<egl::video::Model>    model;
    boost::shared_ptr<btCollisionShape>     shape;
    btRigidBody*                            body;

public:
    Ship(const std::string &specifics, const egl::core::Matrix4x4 &view): SpaceObject(), view(view) {
        this->loaded = loadSpecifics(specifics);
    }

    ~Ship() {
        delete body;
    }

    inline bool isLoaded() const {return loaded;}

    /*inline void setView(const egl::core::Matrix4x4 &view) {
        this->view = view;
        egl::video::Model* model = this->model.get();
        if (model != NULL)
            model->view = view;
    }*/

    inline btRigidBody* getBody() { return body; }

    inline void update(int clock) {
        egl::video::Model* model = this->model.get();
        if (model != NULL)
            model->update(clock);
    }

    inline void draw() {
        egl::video::Model* model = this->model.get();
        if (model != NULL) {
            model->view = view;
            model->draw();
        }
    }

private:
    inline bool loadSpecifics(const std::string &specifics) {
        egl_call("Ship.loadSpecifics");
        std::string tmps, fileName = egl::sys::Settings::findFile(egl::sys::Settings::getArray("paths.specifics"), specifics);
        if (fileName == "")
            return false;
        jsl::Node* node = jsl::Node::parseJslFile(fileName);
        if (node == NULL)
            return false;

        this->name = node->at("name")->asString();

        tmps = node->at("model")->asString();
        if (!ds::Models::has(tmps)) {
            fileName = egl::sys::Settings::findFile(egl::sys::Settings::getArray("paths.models"), tmps);
            if (fileName == "") {
                delete node;
                return false;
            }
            boost::shared_ptr<egl::video::Model> model_ptr(new egl::video::Model(fileName));
            ds::Models::add(tmps, model_ptr);
        }
        this->model = ds::Models::get(tmps);

        btScalar mass;
        try {
            mass = node->at("mass")->asDouble();
        } catch (std::exception &e) {
            mass = 1.0;
            egl_log("Property 'mass' is undefined or invalid.");
        }

        tmps = node->at("shape")->asString();
        if (!ds::Shapes::has(tmps)) {
            fileName = egl::sys::Settings::findFile(egl::sys::Settings::getArray("paths.shapes"), tmps);
            if (fileName == "") {
                delete node;
                return false;
            }
            jsl::Node* tmpnode = jsl::Node::parseJslFile(fileName);
            if (tmpnode != NULL) {
                btCollisionShape* cshape = ds::Shapes::createFromJSL(tmpnode, this->model.get());
                btVector3 tmp_zero_vector(7.0, 7.0, 3.0);
                cshape->calculateLocalInertia(mass, tmp_zero_vector);
                if (cshape != NULL) {
                    boost::shared_ptr<btCollisionShape> shape_ptr(cshape);
                    ds::Shapes::add(tmps, shape_ptr);
                }
                delete tmpnode;
            }
        }
        this->shape = ds::Shapes::get(tmps);

        btRigidBody::btRigidBodyConstructionInfo rbci(mass, this, this->shape.get(), btVector3(7.0, 7.0, 3.0));
        rbci.m_linearSleepingThreshold = 0.01;
        rbci.m_angularSleepingThreshold = 0.1;
        rbci.m_linearDamping = 0.01;
        rbci.m_angularDamping = 0.01;
        rbci.m_friction = 5.0;
        rbci.m_restitution = 0.5;
        body = new btRigidBody(rbci);

        delete node;
        return true;
    }



public:
    /// btMotionState implementation
    virtual void getWorldTransform(btTransform &worldTrans) const {
        worldTrans = ds::BulletConvert::toBtTransform(view);
    }

    virtual void setWorldTransform(const btTransform &worldTrans) {
        view = ds::BulletConvert::toMatrix4x4(worldTrans);
    }
};

} // namespace ds

#endif
